package com.pgmmers.radar.dal.model.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.pgmmers.radar.dal.bean.ModelQuery;
import com.pgmmers.radar.dal.bean.PageResult;
import com.pgmmers.radar.dal.model.ModelDal;
import com.pgmmers.radar.enums.StatusType;
import com.pgmmers.radar.mapper.*;
import com.pgmmers.radar.mapstruct.*;
import com.pgmmers.radar.model.*;
import com.pgmmers.radar.util.BaseUtils;
import com.pgmmers.radar.vo.model.*;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;


@Service
public class ModelDalImpl implements ModelDal {

    public static Logger logger = LoggerFactory.getLogger(ModelDalImpl.class);

    @Autowired
    private ModelMapper modelMapper;

    @Autowired
    private FieldMapper fieldMapper;

    @Autowired
    private AbstractionMapper abstractionMapper;

    @Autowired
    private ActivationMapper activationMapper;

    @Autowired
    private DataListsMapper dataListMapper;

    @Autowired
    private DataListMetaMapper dataListMetaMapper;

    @Autowired
    private DataListRecordMapper dataListRecordMapper;

    @Autowired
    private RuleMapper ruleMapper;

    @Autowired
    private PreItemMapper preItemMapper;
    @Autowired
    private PreItemMapping preItemMapping;
    @Resource
    private ModelMapping modelMapping;
    @Resource
    private FieldMapping fieldMapping;
    @Resource
    private RuleMapping ruleMapping;
    @Resource
    private AbstractionMapping abstractionMapping;
    @Resource
    private ActivationMapping activationMapping;

    @Cacheable(value = "models", key = "#guid")
    @Override
    public ModelVO getModelByGuid(String guid) {
        logger.info("getModelByGuid,{}", guid);

        ModelPO po = modelMapper.selectOne(
                Wrappers.lambdaQuery(ModelPO.class).eq(ModelPO::getGuid,guid)
        );
        if (po == null ) {
            return null;
        }
        return modelMapping.sourceToTarget(po);
    }

    //@Cacheable(value = "models", key = "#modelId")
    @Override
    public ModelVO getModelById(Long modelId) {
        logger.info("getModelById,{}", modelId);
        ModelPO model = modelMapper.selectById(modelId);
        if (model != null) {
            return modelMapping.sourceToTarget(model);
        }
        return null;
    }

    @Override
    public List<AbstractionVO> listAbstraction(Long modelId) {
        logger.info("listAbstraction,{}", modelId);
        return listAbstraction(modelId, null);
    }

    @Override
    public List<ActivationVO> listActivation(Long modelId) {
        logger.info("listActivation,{}", modelId);
        return listActivation(modelId, null);
    }

    @Override
    public List<FieldVO> listField(Long modelId) {
        logger.info("listField,{}", modelId);
        List<FieldPO> list = fieldMapper.selectList(
                Wrappers.lambdaQuery(FieldPO.class).eq(FieldPO::getModelId,modelId)
        );
        if (CollectionUtils.isEmpty(list)) {
            return new ArrayList<>();
        } else {
            return fieldMapping.sourceToTarget(list);
        }
    }

    @Override
    public List<ModelVO> listModel(String merchantCode, Integer status) {
        logger.info("listAbstraction,merchantCode:{},status:{}", merchantCode, status);
        return listModel(merchantCode, status, null);
    }

    @Override
    public List<ModelVO> listModel(Integer status) {
        logger.info("listAbstraction,status:{}", status);
        LambdaQueryWrapper<ModelPO> wrapper = Wrappers.lambdaQuery(ModelPO.class);
        if (status != null) {
            wrapper.eq(ModelPO::getStatus, status);
        }
        List<ModelPO> list = modelMapper.selectList(wrapper);
        if (CollectionUtils.isEmpty(list)) {
            return new ArrayList<>();
        }
        return modelMapping.sourceToTarget(list);
    }

    @Override
    public List<AbstractionVO> listAbstraction(Long modelId, Integer status) {
        logger.info("listAbstraction,{}", modelId);
        LambdaQueryWrapper<AbstractionPO> wrapper = Wrappers.lambdaQuery(AbstractionPO.class)
                .eq(AbstractionPO::getModelId, modelId);
        if (status != null) {
            wrapper.eq(AbstractionPO::getStatus, status);
        }
        List<AbstractionPO> list = abstractionMapper.selectList(wrapper);
        if (CollectionUtils.isEmpty(list)) {
            return new ArrayList<>();
        }
        return abstractionMapping.sourceToTarget(list);

    }

    @Override
    public List<ActivationVO> listActivation(Long modelId, Integer status) {
        logger.info("listActivation,{}", modelId);
        LambdaQueryWrapper<ActivationPO> wrapper = Wrappers.lambdaQuery(ActivationPO.class)
                .eq(ActivationPO::getModelId, modelId);
        if (status != null) {
            wrapper.eq(ActivationPO::getStatus, status);
        }
        List<ActivationPO> list = activationMapper.selectList(wrapper);
        if (CollectionUtils.isEmpty(list)) {
            return new ArrayList<>();
        }
        return activationMapping.sourceToTarget(list);
    }


    @Override
    public List<RuleVO> listRules(Long modelId, Long activationId, Integer status) {
        logger.info("listRules:{},{},{}", modelId, activationId, status);
        LambdaQueryWrapper<RulePO> wrapper = Wrappers.lambdaQuery(RulePO.class);
        if (modelId != null) {
            wrapper.eq( RulePO::getModelId, modelId);
        }
        if (activationId != null) {
            wrapper.eq(RulePO::getActivationId, activationId);
        }
        if (status != null) {
            wrapper.eq(RulePO::getStatus, status);
        }
        List<RulePO> rulePoList = ruleMapper.selectList(wrapper);
        if (CollectionUtils.isEmpty(rulePoList)) {
            return new ArrayList<>();
        }
        return ruleMapping.sourceToTarget(rulePoList);
    }

    @Override
    public List<PreItemVO> listPreItem(Long modelId, Integer status) {
        logger.info("listRules:{},{}", modelId, status);
        LambdaQueryWrapper<PreItemPO> wrapper = Wrappers.lambdaQuery(PreItemPO.class);
        wrapper.eq(PreItemPO::getModelId, modelId);
        if (status != null) {
            wrapper.eq(PreItemPO::getStatus, status);
        }
        List<PreItemPO> itemPOList = preItemMapper.selectList(wrapper);
        if (CollectionUtils.isEmpty(itemPOList)){
            return new ArrayList<>();
        }
        return preItemMapping.sourceToTarget(itemPOList);
    }

    @Override
    public PageResult<ModelVO> query(ModelQuery query) {
        PageHelper.startPage(query.getPageNo(), query.getPageSize());
        LambdaQueryWrapper<ModelPO> wrapper = Wrappers.lambdaQuery(ModelPO.class);
        if (!StringUtils.isEmpty(query.getModelName())) {
            wrapper.like(ModelPO::getModelName, BaseUtils.buildLike(query.getModelName()));
        }
        if (!StringUtils.isEmpty(query.getLabel())) {
            wrapper.like(ModelPO::getLabel, BaseUtils.buildLike(query.getLabel()));
        }
        if (query.getStatus() != null) {
            wrapper.eq(ModelPO::getStatus, query.getStatus());
        }
        if (!StringUtils.isEmpty(query.getMerchantCode())) {
            wrapper.eq(ModelPO::getCode, query.getMerchantCode());
        }
        List<ModelPO> list = modelMapper.selectList(wrapper);
        Page<ModelPO> page = (Page<ModelPO>) list;
        List<ModelVO> listVO;
        if (CollectionUtils.isEmpty(list)) {
            listVO = new ArrayList<>();
        } else {
            listVO = modelMapping.sourceToTarget(list);
        }
        return new PageResult<>(page.getPageNum(), page.getPageSize(),
                (int) page.getTotal(), listVO);
    }

    //@CachePut(value = "models", key = "#model.id")
    @Override
    public int save(ModelVO model) {
        ModelPO modelPO = modelMapping.targetToSource(model);
        Date sysDate = new Date();
        int count = 0;
        if (model.getId() == null) {
            modelPO.setCreateTime(sysDate);
            modelPO.setUpdateTime(sysDate);
            count = modelMapper.insert(modelPO);
            model.setId(modelPO.getId());//返回id
        } else {
            modelPO.setUpdateTime(sysDate);
            count = modelMapper.updateById(modelPO);
        }
        return count;
    }

    @Override
    public int delete(Long[] id) {
        int count = modelMapper.delete(
                Wrappers.lambdaQuery(ModelPO.class).in(ModelPO::getId,List.of(id))
        );
        // 删除关联子表
        fieldMapper.delete(Wrappers.lambdaQuery(FieldPO.class).in(FieldPO::getModelId,List.of(id)));

        preItemMapper.delete(Wrappers.lambdaQuery(PreItemPO.class).in(PreItemPO::getModelId,List.of(id)));

        dataListMapper.delete(Wrappers.lambdaQuery(DataListsPO.class).in(DataListsPO::getModelId,List.of(id)));

        abstractionMapper.delete(Wrappers.lambdaQuery(AbstractionPO.class).eq(AbstractionPO::getModelId,List.of(id)));

        activationMapper.delete(Wrappers.lambdaQuery(ActivationPO.class).in(ActivationPO::getModelId,List.of(id)));

        ruleMapper.delete(Wrappers.lambdaQuery(RulePO.class).in(RulePO::getModelId,List.of(id)));

        return count;
    }

    @Override
    public int copy(ModelVO model) {
        Long oldId = model.getId();
        model.setId(null);
        model.setStatus(StatusType.INIT.getKey());
        model.setGuid(UUID.randomUUID().toString().toUpperCase());
        model.setTemplate(false);
        int count = this.save(model);

        Long newId = model.getId();
        Date sysDate = new Date();

        //复制field
        List<FieldPO> fieldList = fieldMapper.selectList(
                Wrappers.lambdaQuery(FieldPO.class).eq(FieldPO::getModelId,oldId)
        );
        for (FieldPO po : fieldList) {
            po.setModelId(newId);
            po.setId(null);
            po.setCreateTime(sysDate);
            po.setUpdateTime(sysDate);
            fieldMapper.insert(po);
        }

        //复制preItem
        List<PreItemPO> preItemList = preItemMapper.selectList(
                Wrappers.lambdaQuery(PreItemPO.class).eq(PreItemPO::getModelId,oldId)
        );
        for (PreItemPO po : preItemList) {
            po.setModelId(newId);
            po.setId(null);
            po.setCreateTime(sysDate);
            po.setUpdateTime(sysDate);
            preItemMapper.insert(po);
        }

        //复制dataList
        List<DataListsPO> dataListsList = dataListMapper.selectList(
                Wrappers.lambdaQuery(DataListsPO.class).eq(DataListsPO::getModelId,oldId)
        );
        for (DataListsPO po : dataListsList) {
            Long oldDataListId = po.getId();

            po.setModelId(newId);
            po.setId(null);
            po.setCreateTime(sysDate);
            po.setUpdateTime(sysDate);
            dataListMapper.insert(po);

            Long newDataListId = po.getId();

            //复制dataListMeta
            List<DataListMetaPO> dataListMetaList = dataListMetaMapper.selectList(
                    Wrappers.lambdaQuery(DataListMetaPO.class).eq(DataListMetaPO::getDataListId,oldDataListId)
            );
            for (DataListMetaPO metaPO : dataListMetaList) {
                metaPO.setDataListId(newDataListId);
                metaPO.setId(null);
                metaPO.setCreateTime(sysDate);
                metaPO.setUpdateTime(sysDate);
                dataListMetaMapper.insert(metaPO);
            }

            //复制dataListRecord
            List<DataListRecordPO> dataListRecordList = dataListRecordMapper.selectList(
                    Wrappers.lambdaQuery(DataListRecordPO.class).eq(DataListRecordPO::getDataListId,oldDataListId)
            );
            for (DataListRecordPO recordPO : dataListRecordList) {
                recordPO.setDataListId(newDataListId);
                recordPO.setId(null);
                recordPO.setCreateTime(sysDate);
                recordPO.setUpdateTime(sysDate);
                dataListRecordMapper.insert(recordPO);
            }
        }

        //复制abstraction
        List<AbstractionPO> abstractionList = abstractionMapper.selectList(
                Wrappers.lambdaQuery(AbstractionPO.class).eq(AbstractionPO::getModelId,oldId)
        );
        for (AbstractionPO po : abstractionList) {
            po.setModelId(newId);
            po.setId(null);
            po.setCreateTime(sysDate);
            po.setUpdateTime(sysDate);
            abstractionMapper.insert(po);
        }

        //复制activation
        List<ActivationPO> activationList = activationMapper.selectList(
                Wrappers.lambdaQuery(ActivationPO.class).eq(ActivationPO::getModelId,oldId)
        );
        for (ActivationPO po : activationList) {
            Long oldActivationID = po.getId();

            po.setModelId(newId);
            po.setId(null);
            po.setCreateTime(sysDate);
            po.setUpdateTime(sysDate);
            activationMapper.insert(po);

            Long newActivationID = po.getId();

            //复制rule
            List<RulePO> ruleList = ruleMapper.selectList(
                    Wrappers.lambdaQuery(RulePO.class).eq(RulePO::getActivationId,oldActivationID)
            );
            for (RulePO rulePO : ruleList) {
                rulePO.setModelId(newId);
                rulePO.setActivationId(newActivationID);
                rulePO.setId(null);
                rulePO.setCreateTime(sysDate);
                rulePO.setUpdateTime(sysDate);
                ruleMapper.insert(rulePO);
            }
        }

        return count;
    }

    @Override
    public List<ModelVO> listModel(String code, Integer status,
            Boolean isTemplate) {
        LambdaQueryWrapper<ModelPO> wrapper = Wrappers.lambdaQuery(ModelPO.class);
        if (!StringUtils.isEmpty(code)) {
            wrapper.eq(ModelPO::getCode, code);
        }
        if (status != null) {
            wrapper.eq(ModelPO::getStatus, status);
        }
        if (isTemplate != null) {
            wrapper.eq(ModelPO::getTemplate, isTemplate);
        }
        List<ModelPO> list = modelMapper.selectList(wrapper);
        if (list == null) {
            return new ArrayList<>();
        }
        return modelMapping.sourceToTarget(list);
    }

}
